package work.timejay.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.time.format.FormatStyle;
import java.util.List;

/**
 * @className:BigFileUpload.java
 * @author:王文杰
 * @version:1.0.0
 * @description:TODO
 * @createTime 2022年11月17日 14:54:00
 */
@Slf4j
@Component("BigFileUploadUtils")
public class BigFileUploadUtils {
    //分片，文件存储地址
    private static String FILE_SAVE_PATH = "C:\\fileTest";
    private static String FILE_SLICE_TOTAL_NUM = "chunks";
    private static String CURRENT_FILE_SLICE_NUM = "chunk";
    private static String SLICE_FILE_NAME = "name";
    private static String UTF_8 = "utf-8";

    /**
     * 分片上传
     * 注意 springBoot自身有上传组件，使用common-upload 需要在配置文件，或者启动类上排除
     * 开启分片，第一次请求，为普通的表单请求，传递，分片总数，当前分片，文件名称等！
     *
     * @param request
     * @param response
     * @return
     */
    public Boolean bigFileSliceUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //获取文件名称，总分片，当前分片
        String sliceFileName = null;
        Integer fileSliceTotalNum = null;
        Integer currentFileSliceNum = null;
        //文件写出流
        BufferedOutputStream bos = null;
        //开始处理分片
        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //文件写入大小
            //factory.setSizeThreshold(10 * 1024 * 1024 * 1024);
            factory.setSizeThreshold(1024);
            //设置存储位置
            factory.setRepository(new File(FILE_SAVE_PATH));
            ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
            //设置单个分片文件最大值
            servletFileUpload.setFileSizeMax(5L * 1024L * 1024L * 1024L);
            //设置单次上传文件最大值
            servletFileUpload.setSizeMax(10L * 1024L * 1024L * 1024L);
            List<FileItem> fileList = servletFileUpload.parseRequest(request);
            //List<FileItem> fileList = getServletFileUpload(request);
            for (FileItem e : fileList) {
                //校验是否为表单请求
                if (!e.isFormField()) {
                    //跳过
                    continue;
                }
                if (SLICE_FILE_NAME.equals(e.getFieldName())) {
                    sliceFileName = e.getString(UTF_8);
                }
                if (FILE_SLICE_TOTAL_NUM.equals(e.getFieldName())) {
                    fileSliceTotalNum = Integer.valueOf(e.getString(UTF_8));
                }
                if (CURRENT_FILE_SLICE_NUM.equals(e.getFieldName())) {
                    currentFileSliceNum = Integer.valueOf(e.getString(UTF_8));
                }
            }
            //分片处理
            for (FileItem e : fileList) {
                if (e.isFormField()) {
                    continue;
                }
                //校验分片是否存在
                if (sliceFileName == null || currentFileSliceNum == null) {
                    continue;
                }
                //指定目录/以及文件名称
                File sliceFile = new File(FILE_SAVE_PATH, (currentFileSliceNum + "_" + sliceFileName));
                if (sliceFile.exists()) {
                    continue;
                }
                currentFileSliceNum += 1;
                e.write(sliceFile);
            }
            //分片合并
            if (currentFileSliceNum.equals((fileSliceTotalNum - 1))) {
                //输出流
                bos = new BufferedOutputStream(new FileOutputStream(new File(FILE_SAVE_PATH, sliceFileName)));
                //获取所有分片
                for (Integer i = 0; i < fileSliceTotalNum; i++) {
                    String fileName = (i + "_" + sliceFileName);
                    //多线程，校验文件是否存在，不存在等待100毫秒
                    File sliceFile = new File(FILE_SAVE_PATH, fileName);
                    while (!sliceFile.exists()) {
                        log.error("睡眠100毫秒");
                        Thread.sleep(100);
                        sliceFile = new File(FILE_SAVE_PATH, fileName);
                    }
                    //写入到byte数组
                    byte[] bytes = FileUtil.readBytes(sliceFile);
                    bos.write(bytes);
                    bos.flush();
                    sliceFile.delete();
                }
                bos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return Boolean.FALSE;
        } finally {
            if (null != bos) {
                bos.close();
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 公共配置
     *
     * @return
     */
    private List<FileItem> getServletFileUpload(HttpServletRequest request) throws FileUploadException {
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //文件写入大小
        //factory.setSizeThreshold(10 * 1024 * 1024 * 1024);
        factory.setSizeThreshold(1024);
        //设置存储位置
        factory.setRepository(new File(FILE_SAVE_PATH));
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        //设置单个分片文件最大值
        servletFileUpload.setFileSizeMax(5L * 1024L * 1024L * 1024L);
        //设置单次上传文件最大值
        servletFileUpload.setSizeMax(10L * 1024L * 1024L * 1024L);
        return servletFileUpload.parseRequest(request);
    }


    /**
     * 大文件上传
     *
     * @param name   文件名称
     * @param chunks 总分片
     * @param chunk  当前分片
     * @param file   文件
     */
    public static void toBigUpload(String name, Integer chunks, Integer chunk, MultipartFile file) throws IOException, InterruptedException {
        //是否需要分片上传
        if (chunks < 0 || chunk != null || name == null) {
            return;
        }
        //校验文件是否存在
        File sliceFile = new File(FILE_SAVE_PATH, (chunk + "_" + name));
        if (sliceFile.exists()) {
            return;
        }
        file.transferTo(sliceFile);
        //合并分片
        if (chunk.equals((chunks - 1))) {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(FILE_SAVE_PATH, name)));
            for (Integer i = 0; i < chunks; i++) {
                String tempSliceFileName = (i + "_" + name);
                File tempSliceFile = new File(FILE_SAVE_PATH, (i + "_" + tempSliceFileName));
                while (!tempSliceFile.exists()) {
                    log.error("文件不存在，睡100毫秒---{}", tempSliceFileName);
                    Thread.sleep(100);
                    tempSliceFile = new File(FILE_SAVE_PATH, tempSliceFileName);
                }
                byte[] bytes = IoUtil.readBytes(new FileInputStream(tempSliceFile));
                bos.write(bytes);
                bos.flush();
                tempSliceFile.delete();
            }
            bos.flush();
            bos.close();
        }
    }
}
